// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#ifndef __C_D3D9_PARALLAX_MAPMATERIAL_RENDERER_H_INCLUDED__
#define __C_D3D9_PARALLAX_MAPMATERIAL_RENDERER_H_INCLUDED__

#include "IrrCompileConfig.h"
#ifdef _IRR_WINDOWS_

#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
#if defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
#include "irrMath.h"    // needed by borland for sqrtf define
#endif
#include <d3d9.h>

#include "CD3D9ShaderMaterialRenderer.h"
#include "IShaderConstantSetCallBack.h"

#include "IVideoDriver.h"
#include "IMaterialRendererServices.h"
#include "SLight.h"

namespace irr
{
namespace video
{

#ifdef DELPHI_WRAP
	class CD3D9ParallaxMapRendererCallBack : public IShaderConstantSetCallBack
	{
	private:
		f32 &destScale;
	public:
		CD3D9ParallaxMapRendererCallBack(f32 &DestScale) : destScale(DestScale)
		{

		}

		virtual void _STDCALL_ OnSetMaterial(const SMaterial& material) 
		{
		}

		virtual void _STDCALL_ OnSetConstants(IMaterialRendererServices* services, s32 userData)
		{
			video::IVideoDriver* driver = services->getVideoDriver();

			// set transposed world matrix
			services->setVertexShaderConstant(driver->getTransform(video::ETS_WORLD).getTransposed().pointer(), 0, 4);

			// set eye position

			// The  viewpoint is at (0., 0., 0.) in eye space.
			// Turning this into a vector [0 0 0 1] and multiply it by
			// the inverse of the view matrix, the resulting vector is the
			// object space location of the camera.

			f32 floats[4] = {0,0,0,1};
			core::matrix4 minv = driver->getTransform(video::ETS_VIEW);
			minv.makeInverse();
			minv.multiplyWith1x4Matrix(floats);
			services->setVertexShaderConstant(floats, 4, 1);

			// set transposed worldViewProj matrix
			core::matrix4 worldViewProj;
			worldViewProj = driver->getTransform(video::ETS_PROJECTION);
			worldViewProj *= driver->getTransform(video::ETS_VIEW);
			worldViewProj *= driver->getTransform(video::ETS_WORLD);
			services->setVertexShaderConstant(worldViewProj.getTransposed().pointer(), 8, 4);

			// here we've got to fetch the fixed function lights from the
			// driver and set them as constants

			const u32 cnt = driver->getDynamicLightCount();

			for (u32 i=0; i<2; ++i)
			{
				SLight light;

				if (i<cnt)
					light = driver->getDynamicLight(i);
				else
				{
					light.DiffuseColor.set(0,0,0); // make light dark
					light.Radius = 1.0f;
				}

				light.DiffuseColor.a = 1.0f/(light.Radius*light.Radius); // set attenuation

				services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.Position), 12+(i*2), 1);
				services->setVertexShaderConstant(reinterpret_cast<const f32*>(&light.DiffuseColor), 13+(i*2), 1);
			}

			// this is not really necessary in d3d9 (used a def instruction), but to be sure:
			f32 c95[] = {0.5f, 0.5f, 0.5f, 0.5f};
			services->setVertexShaderConstant(c95, 95, 1);
			f32 c96[] = {-1, 1, 1, 1};
			services->setVertexShaderConstant(c96, 96, 1);

			// set scale factor
			f32 factor = 0.02f; // default value
			if (destScale != 0)
				factor = destScale;

			f32 c6[] = {factor, factor, factor, 0};
			services->setPixelShaderConstant(c6, 6, 1);
		}
	};
#endif


//! Renderer for normal maps using parallax mapping
#ifdef DELPHI_WRAP
	class CD3D9ParallaxMapRenderer : public CD3D9ShaderMaterialRenderer
#else
	class CD3D9ParallaxMapRenderer :
		public CD3D9ShaderMaterialRenderer, IShaderConstantSetCallBack
#endif
{

#ifdef DELPHI_WRAP
private:
	CD3D9ParallaxMapRendererCallBack * ShaderCallBack;
#endif

public:
	CD3D9ParallaxMapRenderer(
		IDirect3DDevice9* d3ddev, video::IVideoDriver* driver,
		s32& outMaterialTypeNr, IMaterialRenderer* baseMaterial);

	~CD3D9ParallaxMapRenderer();

	//! Called by the engine when the vertex and/or pixel shader constants for an
	//! material renderer should be set.
	virtual void _STDCALL_ OnSetConstants(IMaterialRendererServices* services, s32 userData);

	virtual bool _STDCALL_ OnRender(IMaterialRendererServices* service, E_VERTEX_TYPE vtxtype);

	//! Returns the render capability of the material.
	virtual s32 _STDCALL_ getRenderCapability() const;

#ifdef DELPHI_WRAP
#else
	virtual void _STDCALL_ OnSetMaterial(const SMaterial& material) { }
#endif
	
	virtual void _STDCALL_ OnSetMaterial(const video::SMaterial& material,
		const video::SMaterial& lastMaterial,
		bool resetAllRenderstates, video::IMaterialRendererServices* services);

////#ifdef DELPHI_WRAP
////	virtual bool _STDCALL_ getShaderCallBack(IShaderConstantSetCallBack ** callback)
////	{
////		*callback = CallBack;
////		return (*callback != 0);
////	}
////#endif

private:

	f32 CurrentScale;

};

} // end namespace video
} // end namespace irr

#endif
#endif
#endif

